package conf

// 此文件，用于定义程序配置

import (
	"encoding/json"
	"fmt"
	"sync"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 定义程序配置
type Config struct {
	// `toml:"mysql"`不写也可以，因为做toml转化的是结构体Mysql
	Mysql *Mysql `toml:"mysql"`
	Api   *Api
}

// 让Config实现stringer方法,不然输出的是指针
func (c *Config) String() string {
	js, _ := json.Marshal(c)
	return string(js)
}

// 为了在main.go中使用
type Api struct {
	Apihost string `json:"apihost"`
	Apiport int64  `json:"apiport"`
}
type Mysql struct {
	Host     string `json:"host" toml:"host" env:"Host"`
	Port     int    `json:"port" toml:"port" env:"Port"`
	Database string `json:"database" toml:"database"`
	Username string `json:"username" toml:"username"`
	Password string `json:"password" toml:"password"`
	// 缓存db对象，此属性不对外使用，所以用的小写
	conn *gorm.DB
	// 加入互斥锁
	lock sync.Mutex
}

// 定义默认程序配置
func DefaultConfig() *Config {
	return &Config{
		Mysql: &Mysql{
			Host:     "localhost",
			Port:     3306,
			Database: "vblog",
			Username: "vblog",
			Password: "123456",
		},
		Api: &Api{
			Apihost: "127.0.0.1",
			Apiport: 8090,
		},
	}
}

// 实现grom,通过Mysql来扩展方法
// dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
func (m *Mysql) Dns() string {
	return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		m.Username, m.Password, m.Host, m.Port, m.Database,
	)

}

// 每次调用此函数时，都会产生一个gorm.DB，所以要改进代码，如何改进？
// 在对象中缓存一个db，如果对象中有缓存对象就不生产新的db，如果没有在生成db
// 如果多个gorouting调用此方法，会产生脏数据，如何解决?
// 在对象中加锁
func (m *Mysql) Getconn() *gorm.DB {
	// 要把锁的位置放到最顶层，因为在调用这个函数时，就上锁
	m.lock.Lock()
	defer m.lock.Unlock()
	// 对象中没有db就赋值
	if m.conn == nil {
		// 加锁
		// m.lock.Lock()
		// 解锁
		// defer m.lock.Unlock()
		db, err := gorm.Open(mysql.Open(m.Dns()), &gorm.Config{})
		if err != nil {
			// 如果数据库连接不上，直接panic
			panic(err)
		}
		m.conn = db
	}
	return m.conn
}
